package org.xlp.mv;

import org.xlp.assertion.AssertUtils;
import org.xlp.db.exception.EntityException;
import org.xlp.db.page.Page;
import org.xlp.db.sql.*;
import org.xlp.db.tableoption.handlers.result.DataSet;
import org.xlp.db.utils.PagedQuerySQLHelper;
import org.xlp.utils.XLPArrayUtil;

import java.util.*;

/**
 * 
 * @author 徐龙平
 * 
 */
public interface IBaseService {
	/**
	 * 向数据库中插入一条数据
	 * <p>
	 * 假如给定要存储的对象是实体，并有相关的主键，并主键值未指定，那么此函数会自动生成一个可用的主键值
	 * 
	 * @param bean
	 * @return 主键值，假如返回null时，可能数据保存失败，也可能指定的bean对象没有主键属性。 假如复合主键时，返回他们组成的数组
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 */
	<T> Object save(T bean);

	/**
	 * 向数据库中插入多条数据
	 * <p>
	 * 假如给定要存储的对象是实体，并有相关的主键，并主键值未指定，那么此函数会自动生成一个可用的主键值
	 * 
	 * @param beanList
	 *            数据集合
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 */
	<T> int[] save(List<T> beanList);

	/**
	 * 向数据库中插入多条数据
	 * <p>
	 * 假如给定要存储的对象是实体，并有相关的主键，并主键值未指定，那么此函数会自动生成一个可用的主键值
	 * 
	 * @param beanArray
	 *            数据数组
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 */
	default <T> int[] save(T[] beanArray){
		if (XLPArrayUtil.isEmpty(beanArray)) return new int[0];
		return save(new ArrayList<>(Arrays.asList(beanArray)));
	}

	/**
	 * 通过SQL对象向数据库中插入一条数据
	 * 
	 * @param sqlObj
	 *            SQL对象
	 * @return 受影响数据的条数
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	int save(Insert sqlObj);

	/**
	 * 得到单表数据的条数
	 * 
	 * @param beanClass
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 */
	<T> long count(Class<T> beanClass);

	/**
	 * 得数据的条数
	 * 
	 * @param sqlObj
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	long count(Statistics sqlObj);

	/**
	 * 通过SQL对象得到javabean对象
	 * 
	 * @param sqlObj
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	<T> T find(Query sqlObj);

	/**
	 * 通过 实体对象得到javabean对象
	 * 
	 * @param bean
	 *            实体对象,此对象一定要有相关的主属性，否则抛出SQL异常
	 * @return 假如参数为null，返回null，没有符合要求的数据也返回null
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> T find(T bean);

	/**
	 * 通过主键查找数据
	 * 
	 * @param beanClass
	 *            对象类型
	 * @param keyValue
	 *            主键属性值(单主键)
	 * @return 假如参数为null，返回null，没有符合要求的数据也返回null
	 * @throws EntityException OptionDBException 
	 *             假如该对象类型对象不是实体或数据库访问出错时，抛出该异常
	 */
	<T> T find(Class<T> beanClass, Object keyValue);

	/**
	 * 通过主键查找数据
	 * 
	 * @param beanClass
	 *            对象类型
	 * @param keyValues
	 *            主键属性值数组(复合主键)
	 * @return 假如参数为null，返回null，没有符合要求的数据也返回null
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> T find(Class<T> beanClass, Object... keyValues);

	/**
	 * 通过单表得到javabean list集合
	 * 
	 * @param beanClass
	 *            bean类型
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 */
	<T> List<T> list(Class<T> beanClass);

	/**
	 * 通过SQL对象得到javabean list集合
	 * 
	 * @param sqlObj
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	<T> List<T> list(Query sqlObj);

	/**
	 * 通过单表得到javabean set集合
	 * 
	 * @param beanClass
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 */
	<T> Set<T> set(Class<T> beanClass);

	/**
	 * 通过SQL对象得到javabean set集合
	 * 
	 * @param sqlObj
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	<T> Set<T> set(Query sqlObj);

	/**
	 * 删除指定bean类型对应的表中的所有数据
	 * 
	 * @param beanClass
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	<T> int delete(Class<T> beanClass);

	/**
	 * 删除指定bean对应的表中的数据
	 * 
	 * @param bean
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	<T> int delete(T bean);

	/**
	 * 根据SQL对象更新数据
	 * 
	 * @param sql
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	int update(Update sql);

	/**
	 * 根据SQL对象删除数据
	 *
	 * @param sql
	 * @return
	 * @throws EntityException OptionDBException
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	int delete(Delete sql);

	/**
	 * 通过主键删除数据
	 * 
	 * @param beanClass
	 *            对象类型
	 * @param keyValue
	 *            主键属性值(单主键)
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> int delete(Class<T> beanClass, Object keyValue);

	/**
	 * 通过主键删除数据(复合主键)
	 * 
	 * @param beanClass
	 *            对象类型
	 * @param keyValues
	 *            主键属性值数组(复合主键)
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> int delete(Class<T> beanClass, Object... keyValues);

	/**
	 * 更新，指定的bean一定要主键属性
	 * 
	 * @param bean
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> int update(T bean);

	/**
	 * 批量更新，指定的bean一定要主键属性
	 *
	 * @param beanList
	 * @return
	 * @throws EntityException OptionDBException
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> int[] update(List<T> beanList);

	/**
	 * 批量更新，指定的bean一定要主键属性
	 *
	 * @param beanArr
	 * @return
	 * @throws EntityException OptionDBException
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	default  <T> int[] update(T[] beanArr){
		if (XLPArrayUtil.isEmpty(beanArr)) return new int[0];
		return update(new ArrayList<>(Arrays.asList(beanArr)));
	}

	/**
	 * 通过SQL对象获取dataset对象
	 * 
	 * @param sql
	 * @return 从不返回null
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	DataSet findDataSet(Query sql);

	/**
	 * 把数据处理成数组
	 * 
	 * @param sql
	 * @return 从不返回null, 如无数据返回大小为0的数组
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	Object[] array(Query sql);

	/**
	 * 把数据处理成List<Object[]>集合
	 * 
	 * @param sql
	 * @return 从不返回null, 如无数据返回大小为0的集合
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	List<Object[]> listArray(Query sql);

	/**
	 * 把数据处理成Map<String, Object>集合
	 * 
	 * @param sql
	 * @return 从不返回null, 如无数据返回大小为0的集合
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	Map<String, Object> map(Query sql);

	/**
	 * 把数据处理成List<Map<String, Object>>集合
	 * 
	 * @param sql
	 * @return 从不返回null, 如无数据返回大小为0的集合
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	List<Map<String, Object>> listMap(Query sql);

	/**
	 * 分页查询
	 * 
	 * @param sql
	 *            SQL对象， 该对象中应不包含limit条件，否则可能抛出异常
	 * @param page
	 *            分页对象
	 * @return 返回完整分页对象
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	<T> Page<T> searchPage(Query sql, Page<T> page);

	/**
	 * 分页查询
	 * 
	 * @param sql
	 *            SQL对象， 该对象中应不包含limit条件，否则可能抛出异常
	 * @return 返回默认分页的首页对象
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	default <T> Page<T> searchPage(Query sql){
		return searchPage(sql, new Page<>());
	}

	/**
	 * 分页查询
	 * 
	 * @param sql
	 *            SQL对象， 该对象中应不包含limit条件，否则可能抛出异常
	 * @param start
	 *            分页开始号码，从1开始
	 * @param size
	 *            分页大小
	 * @return 返回默认分页对象
	 * @throws EntityException OptionDBException 
	 * @throws <code>EntityException</code> 假如数据库访问出错时，抛出该异常
	 * @throws <code>NullPointerException</code> 假如参数为null，抛出该异常
	 */
	default  <T> Page<T> searchPage(Query sql, int start, int size){
		return searchPage(sql, new Page<>(start, size));
	}

	/**
	 * 分页查询
	 *
	 * @param entityClass
	 *
	 * @param start
	 *            分页开始号码，从1开始
	 * @param size
	 *            分页大小
	 * @return 返回默认分页对象
	 * @throws EntityException OptionDBException
	 * @throws <code>EntityException</code> 假如数据库访问出错时，抛出该异常
	 * @throws <code>NullPointerException</code> 假如参数为null，抛出该异常
	 */
	default  <T> Page<T> searchPage(Class<T> entityClass, int start, int size){
		AssertUtils.isNotNull(entityClass, "entityClass parameter must be not null!");
		Query query = PagedQuerySQLHelper.getQueryPagedSQL(entityClass);
		return searchPage(query, start, size);
	}

	/**
	 * 分页查询
	 *
	 * @param entityClass
	 * @return 返回默认分页对象
	 * @throws EntityException OptionDBException
	 * @throws <code>EntityException</code> 假如数据库访问出错时，抛出该异常
	 * @throws <code>NullPointerException</code> 假如参数为null，抛出该异常
	 */
	default  <T> Page<T> searchPage(Class<T> entityClass){
		AssertUtils.isNotNull(entityClass, "entityClass parameter must be not null!");
		Query query = PagedQuerySQLHelper.getQueryPagedSQL(entityClass);
		return searchPage(query);
	}
	
	/**
	 * 得到单表数据的指定字段的最大值
	 * 
	 * @param beanClass
	 * @param maxFieldName
	 *            要求最大值字段名称对应的bean的属性名称
	 * @return 从不返回null，假如无数据返回0
	 * @throws EntityException OptionDBException  
	 */
	<T> Double max(Class<T> beanClass, String maxFieldName);
	
	/**
	 * 得到单表数据的最大值
	 * 
	 * @param sqlObj
	 * @return 从不返回null，假如无数据返回0
	 * @throws EntityException OptionDBException  
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	Double max(Statistics sqlObj);

	/**
	 * 得到单表数据的指定字段的最小值
	 * 
	 * @param beanClass
	 * @param minFieldName
	 *            要求最小值字段名称对应的bean的属性名称
	 * @return 从不返回null，假如无数据返回0
	 * @throws EntityException OptionDBException  
	 */
	<T> Double min(Class<T> beanClass, String minFieldName);

	/**
	 * 得到单表数据的最小值
	 * 
	 * @param sqlObj
	 * @return 从不返回null，假如无数据返回0
	 * @throws EntityException OptionDBException  
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	Double min(Statistics sqlObj);

	/**
	 * 得到单表数据的指定字段的平均值
	 * 
	 * @param beanClass
	 * @param avgFieldName
	 *            要求平均值字段名称对应的bean的属性名称
	 * @return 从不返回null，假如无数据返回0
	 * @throws EntityException OptionDBException  
	 */
	<T> Double avg(Class<T> beanClass, String avgFieldName);

	/**
	 * 得到单表数据的平均值
	 * 
	 * @param sqlObj
	 * @return 从不返回null，假如无数据返回0
	 * @throws EntityException OptionDBException 
	 *             假如数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	Double avg(Statistics sqlObj);

	/**
	 * 得到单表数据的指定字段的和
	 * 
	 * @param beanClass
	 * @param sumFieldName
	 *            要求和段名称对应的bean的属性名称
	 * @return 从不返回null，假如无数据返回0
	 * @throws EntityException OptionDBException  
	 */
	<T> Double sum(Class<T> beanClass, String sumFieldName);

	/**
	 * 得到单表数据的和
	 * 
	 * @param sqlObj
	 * @return 从不返回null，假如无数据返回0
	 * @throws EntityException OptionDBException  
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 */
	Double sum(Statistics sqlObj);
	
	/**
	 * 通过单条件查找数据
	 * 
	 * @param beanClass
	 *            对象类型
	 * @param fieldName
	 *            字段名称
	 * @param value
	 * 			  字段对应的值
	 * @return 假如参数为null，返回null，没有符合要求的数据也返回null
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> T find(Class<T> beanClass, String fieldName, Object value);
	
	/**
	 * 通过多条件查找数据
	 * 
	 * @param beanClass
	 *            对象类型
	 * @param parms
	 *            key:字段名称, value: 字段对应的值
	 * @return 假如参数为null，返回null，没有符合要求的数据也返回null
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> T find(Class<T> beanClass, Map<String, Object> parms);
	
	/**
	 * 通过单条件查找数据
	 * 
	 * @param beanClass
	 *            对象类型
	 * @param fieldName
	 *            字段名称
	 * @param value
	 * 			  字段对应的值
	 * @return 假如参数为null，返回null，没有符合要求的数据也返回null
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> List<T> list(Class<T> beanClass, String fieldName, Object value);
	
	/**
	 * 通过多条件查找数据
	 * 
	 * @param beanClass
	 *            对象类型
	 * @param parms
	 *            key:字段名称, value: 字段对应的值
	 * @return 假如参数为null，返回null，没有符合要求的数据也返回null
	 * @throws EntityException OptionDBException 
	 *             假如该对象没要有相关的主属性或不是实体或数据库访问出错时，抛出该异常
	 */
	<T> List<T> list(Class<T> beanClass, Map<String, Object> parms);
	
	/**
	 * 得到单表数据的条数
	 * 
	 * @param beanClass
	 * @param fieldName 统计的字段名称
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 */
	<T> long count(Class<T> beanClass, String fieldName);
	
	/**
	 * 得到单表数据的条数
	 * 
	 * @param beanClass
	 * @param fieldNames 去重统计的字段名称
	 * @return
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 */
	<T> long distinctCount(Class<T> beanClass, String... fieldNames);
	
	/**
	 * 获取对象
	 * 
	 * @param beanClass
	 * @param sql
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 * @return 
	 */
	<T> T find(Class<T> beanClass, ComplexQuerySQL sql);
	
	/**
	 * 获取对象集合
	 * 
	 * @param beanClass
	 * @param sql
	 * @throws EntityException OptionDBException 
	 *             假如该对象不是实体或数据库访问出错时，抛出该异常
	 * @throws NullPointerException
	 *             假如参数为null，抛出该异常
	 * @return 
	 */
	<T> List<T> list(Class<T> beanClass, ComplexQuerySQL sql);
}
